package com.tlgen.zero.utils;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.tlgen.orm.annotation.AsNotField;
import com.tlgen.zero.model.FieldParam;
import com.tlgen.zero.model.RequestMethodAndUrl;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.text.StringEscapeUtils;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.Objects;

public class ReflectionUtils {

    /**
     * 为对象指定属性赋值
     *
     * @param object
     * @param fieldName
     * @param fieldValue
     * @return
     */
    public static void setFieldValue(Object object, String fieldName, Object fieldValue) {
        try {
            Class<?> superclass = object.getClass().getSuperclass();
            if (Objects.nonNull(superclass)) {
                Field declaredField = object.getClass().getSuperclass().getDeclaredField(fieldName);
                declaredField.setAccessible(true);
                if (Objects.equals(fieldName, declaredField.getName())) {
                    declaredField.set(object, fieldValue);
                }
            }
            Field declaredField = object.getClass().getDeclaredField(fieldName);
            declaredField.setAccessible(true);
            if (Objects.equals(fieldName, declaredField.getName())) {
                declaredField.set(object, fieldValue);
            }
        } catch (NoSuchFieldException | IllegalAccessException ignored) {
        }
    }

    public static List<String> getFields(String completeClassName) {
        List<String> stringList = Lists.newArrayList();
        Class<?> aClass = null;
        try {
            aClass = Class.forName(completeClassName);
            for (Field declaredField : aClass.getDeclaredFields()) {
                // 不映射为数据库中字段属性
                AsNotField annotation = declaredField.getAnnotation(AsNotField.class);
                if (Objects.nonNull(annotation)) {
                    continue;
                }
                stringList.add(declaredField.getName());
            }
            if (!Objects.equals("Object", aClass.getSuperclass().getSimpleName())) {
                stringList.addAll(getFields(aClass.getSuperclass().getTypeName()));
            }
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
        return stringList;
    }

    public static Class<?> getClassByCompleteClassName(String completeClassName) {
        Class<?> aClass = null;
        try {
            aClass = Class.forName(completeClassName);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
        return aClass;
    }

    public static Object fieldParamsToObject(List<FieldParam> fieldParams, String completeClassName) {
        if (Objects.nonNull(completeClassName)) {
            Object object;
            try {
                Class<?> aClass = Class.forName(completeClassName);
                object = aClass.newInstance();
                Field[] fields = aClass.getDeclaredFields();
                for (Field field : fields) {
                    fieldParams.forEach(x -> {
                        if (Objects.equals(x.getName(), field.getName())) {
                            field.setAccessible(true);
                            try {
                                field.set(object, doFormatType(field.getType(), x.getValue()));
                            } catch (IllegalAccessException ignored) {
                            }
                        }
                    });
                }
            } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
                throw new RuntimeException(e);
            }
            return object;
        }
        return null;
    }

    /**
     * 格式化属性, 设置正确的数据值类型
     *
     * @param fieldType
     * @param value
     * @return
     */
    private static Object doFormatType(Class<?> fieldType, Object value) {
        if (Objects.equals(fieldType.getTypeName(), Byte.class.getTypeName())) {
            value = Byte.valueOf(String.valueOf(value));
        }
        if (Objects.equals(fieldType.getTypeName(), byte.class.getTypeName())) {
            value = Byte.parseByte(String.valueOf(value));
        }
        if (Objects.equals(fieldType.getTypeName(), Short.class.getTypeName())) {
            value = Short.valueOf(String.valueOf(value));
        }
        if (Objects.equals(fieldType.getTypeName(), short.class.getTypeName())) {
            value = Short.parseShort(String.valueOf(value));
        }
        if (Objects.equals(fieldType.getTypeName(), Integer.class.getTypeName())) {
            value = Integer.valueOf(String.valueOf(value));
        }
        if (Objects.equals(fieldType.getTypeName(), int.class.getTypeName())) {
            if (Objects.isNull(value)) {
                value = 0;
            } else {
                value = Integer.parseInt(String.valueOf(value));
            }
        }
        if (Objects.equals(fieldType.getTypeName(), Long.class.getTypeName())) {
            value = Long.valueOf(String.valueOf(value));
        }
        if (Objects.equals(fieldType.getTypeName(), long.class.getTypeName())) {
            value = Long.parseLong(String.valueOf(value));
        }
        if (Objects.equals(fieldType.getTypeName(), Float.class.getTypeName())) {
            value = Float.valueOf(String.valueOf(value));
        }
        if (Objects.equals(fieldType.getTypeName(), float.class.getTypeName())) {
            value = Float.parseFloat(String.valueOf(value));
        }
        if (Objects.equals(fieldType.getTypeName(), Double.class.getTypeName())) {
            value = Double.valueOf(String.valueOf(value));
        }
        if (Objects.equals(fieldType.getTypeName(), double.class.getTypeName())) {
            value = Double.parseDouble(String.valueOf(value));
        }
        if (Objects.equals(fieldType.getTypeName(), Boolean.class.getTypeName())) {
            value = Boolean.valueOf(String.valueOf(value));
        }
        if (Objects.equals(fieldType.getTypeName(), boolean.class.getTypeName())) {
            if (Objects.isNull(value)) {
                value = 0;
            } else {
                value = Boolean.parseBoolean(String.valueOf(value));
            }
        }
        return value;
    }


    public static Object invokeHttpRequest(RequestMethodAndUrl requestMethodAndUrl) {
        Object result;
        String className = requestMethodAndUrl.getClassName();
        try {
            Class<?> aClass = Class.forName(className);
            if (aClass.isInterface()) { // 如果是接口, 而接口自身不能实例化, 则找到它的实现类进行实例化
                List<String> implClassesByInterfaceClass = ScanUtils.getImplClassesByInterfaceClass(aClass);
                if (CollectionUtils.isNotEmpty(implClassesByInterfaceClass)) {
                    String implClassName = implClassesByInterfaceClass.get(0);
                    aClass = Class.forName(implClassName);
                }
            }
            try {
                Object instance = aClass.newInstance();
                Class<?>[] parameterTypes = null;
                for (Method declaredMethod : aClass.getDeclaredMethods()) {
                    if (Objects.equals(requestMethodAndUrl.getMethodName(), declaredMethod.getName())) {
                        parameterTypes = declaredMethod.getParameterTypes();
                        break;
                    }
                }
                if (Objects.nonNull(parameterTypes) && parameterTypes.length > 0) {
                    if (Objects.nonNull(requestMethodAndUrl.getArgs())) {
                        // 判断方法参数的类型, 并设置正确的值类型
                        correctArgsToType(requestMethodAndUrl, parameterTypes);
                    } else {
                        throw new RuntimeException("参数不能为空");
                    }
                }
                // 精妙: Class<?>[] parameterTypes 可以支持 Class<?>... parameterTypes, 解决反射调用任意一个方法, 不确定方法参数的入参类型和个数
                Method method = aClass.getMethod(requestMethodAndUrl.getMethodName(), parameterTypes);
                result = method.invoke(instance, requestMethodAndUrl.getArgs());
            } catch (InvocationTargetException | InstantiationException | NoSuchMethodException |
                     IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
        return result;
    }

    /**
     * // 判断方法参数的类型, 并设置正确的值类型
     *
     * @param requestMethodAndUrl
     * @param parameterTypes
     */
    private static void correctArgsToType(RequestMethodAndUrl requestMethodAndUrl, Class<?>[] parameterTypes) {
        if (Objects.equals("GET", requestMethodAndUrl.getRequestType())) {
            doFormatType(requestMethodAndUrl, parameterTypes);
        } else if (Lists.newArrayList("POST", "PUT").contains(requestMethodAndUrl.getRequestType())) {
            convertRequestBodyToInstance(requestMethodAndUrl, parameterTypes);
        } else if (Objects.equals("DELETE", requestMethodAndUrl.getRequestType())) {
            convertRequestBodyToInstance(requestMethodAndUrl, parameterTypes);
        }

    }

    /**
     * 格式化属性, 设置正确的数据值类型
     *
     * @param requestMethodAndUrl
     * @param parameterTypes
     */
    private static void doFormatType(RequestMethodAndUrl requestMethodAndUrl, Class<?>[] parameterTypes) {
        for (int i = 0; i < parameterTypes.length; i++) {
            if (Objects.equals(parameterTypes[i].getTypeName(), Byte.class.getTypeName())) {
                requestMethodAndUrl.getArgs()[i] = Byte.valueOf(String.valueOf(requestMethodAndUrl.getArgs()[i]));
            }
            if (Objects.equals(parameterTypes[i].getTypeName(), byte.class.getTypeName())) {
                requestMethodAndUrl.getArgs()[i] = Byte.parseByte(String.valueOf(requestMethodAndUrl.getArgs()[i]));
            }
            if (Objects.equals(parameterTypes[i].getTypeName(), Short.class.getTypeName())) {
                requestMethodAndUrl.getArgs()[i] = Short.valueOf(String.valueOf(requestMethodAndUrl.getArgs()[i]));
            }
            if (Objects.equals(parameterTypes[i].getTypeName(), short.class.getTypeName())) {
                requestMethodAndUrl.getArgs()[i] = Short.parseShort(String.valueOf(requestMethodAndUrl.getArgs()[i]));
            }
            if (Objects.equals(parameterTypes[i].getTypeName(), Integer.class.getTypeName())) {
                requestMethodAndUrl.getArgs()[i] = Integer.valueOf(String.valueOf(requestMethodAndUrl.getArgs()[i]));
            }
            if (Objects.equals(parameterTypes[i].getTypeName(), int.class.getTypeName())) {
                if (Objects.isNull(requestMethodAndUrl.getArgs()[i])) {
                    requestMethodAndUrl.getArgs()[i] = 0;
                } else {
                    requestMethodAndUrl.getArgs()[i] = Integer.parseInt(String.valueOf(requestMethodAndUrl.getArgs()[i]));
                }
            }
            if (Objects.equals(parameterTypes[i].getTypeName(), Long.class.getTypeName())) {
                requestMethodAndUrl.getArgs()[i] = Long.valueOf(String.valueOf(requestMethodAndUrl.getArgs()[i]));
            }
            if (Objects.equals(parameterTypes[i].getTypeName(), long.class.getTypeName())) {
                requestMethodAndUrl.getArgs()[i] = Long.parseLong(String.valueOf(requestMethodAndUrl.getArgs()[i]));
            }
            if (Objects.equals(parameterTypes[i].getTypeName(), Float.class.getTypeName())) {
                requestMethodAndUrl.getArgs()[i] = Float.valueOf(String.valueOf(requestMethodAndUrl.getArgs()[i]));
            }
            if (Objects.equals(parameterTypes[i].getTypeName(), float.class.getTypeName())) {
                requestMethodAndUrl.getArgs()[i] = Float.parseFloat(String.valueOf(requestMethodAndUrl.getArgs()[i]));
            }
            if (Objects.equals(parameterTypes[i].getTypeName(), Double.class.getTypeName())) {
                requestMethodAndUrl.getArgs()[i] = Double.valueOf(String.valueOf(requestMethodAndUrl.getArgs()[i]));
            }
            if (Objects.equals(parameterTypes[i].getTypeName(), double.class.getTypeName())) {
                requestMethodAndUrl.getArgs()[i] = Double.parseDouble(String.valueOf(requestMethodAndUrl.getArgs()[i]));
            }
            if (Objects.equals(parameterTypes[i].getTypeName(), Boolean.class.getTypeName())) {
                requestMethodAndUrl.getArgs()[i] = Boolean.valueOf(String.valueOf(requestMethodAndUrl.getArgs()[i]));
            }
            if (Objects.equals(parameterTypes[i].getTypeName(), boolean.class.getTypeName())) {
                if (Objects.isNull(requestMethodAndUrl.getArgs()[i])) {
                    requestMethodAndUrl.getArgs()[i] = 0;
                } else {
                    requestMethodAndUrl.getArgs()[i] = Boolean.parseBoolean(String.valueOf(requestMethodAndUrl.getArgs()[i]));
                }
            }
        }
    }

    /**
     * 将请求体转换成实际的引用数据参数类型
     *
     * @param requestMethodAndUrl
     * @param parameterTypes
     */
    private static void convertRequestBodyToInstance(RequestMethodAndUrl requestMethodAndUrl, Class<?>[] parameterTypes) {
        Object[] args = requestMethodAndUrl.getArgs();
        if (Objects.equals(0, requestMethodAndUrl.getBodyType())) { // 请求体类型 0.application-json
            String jsonString = JSONObject.toJSONString(args[0]);

            // 转义成 JSON 格式
            jsonString = jsonString.substring(1, jsonString.length() - 1);
            jsonString = jsonString.replace("\\n", " ");
            jsonString = StringEscapeUtils.unescapeJava(jsonString); // 去转义, 如把 \ 去掉

            JSONObject jsonObject = null;
            JSONArray jsonArray = null;
            // 判断是否是集合接口类型
            if (jsonString.startsWith("[")) {
                jsonArray = JSONArray.parseArray(jsonString);
            } else {
                jsonObject = JSONObject.parseObject(jsonString);
            }
            // 反射创建对象
            try {
                Class<?> aClass = Class.forName(parameterTypes[0].getName());
                if (aClass.getTypeName().contains("Collection")) { // 如果是 Collection 接口类型, 则无法直接进行实例化
                    List list = jsonArray;
                    // 设置接口类型参数
                    Object[] instanceArgs = new Object[1];
                    instanceArgs[0] = list;
                    requestMethodAndUrl.setArgs(instanceArgs);
                } else if (aClass.getTypeName().contains("List")) { // 如果是 List 接口类型, 则无法直接进行实例化
                    List list = jsonArray;
                    // 设置接口类型参数
                    Object[] instanceArgs = new Object[1];
                    instanceArgs[0] = list;
                    requestMethodAndUrl.setArgs(instanceArgs);
                } else if (aClass.getTypeName().contains("Map")) { // 如果是 Map 接口类型, 则无法直接进行实例化
                    Map objectMap = jsonObject.toJavaObject(Map.class);
                    // 设置接口类型参数
                    Object[] instanceArgs = new Object[1];
                    instanceArgs[0] = objectMap;
                    requestMethodAndUrl.setArgs(instanceArgs);
                } else { // 如果是普通类对象类型, 可以直接进行实例化
                    Object instance = aClass.newInstance();
                    Field[] declaredFields = aClass.getDeclaredFields();
                    for (Field declaredField : declaredFields) {
                        for (Map.Entry<String, Object> entry : jsonObject.entrySet()) {
                            if (Objects.equals(declaredField.getName(), entry.getKey())) {
                                // 根据字段名称获取指定的 Field 对象
                                Field field = aClass.getDeclaredField(declaredField.getName());
                                field.setAccessible(true);
                                // 设置字段的新值
                                field.set(instance, entry.getValue());
                            }
                        }
                    }
                    // 设置对象类型参数
                    Object[] instanceArgs = new Object[1];
                    instanceArgs[0] = instance;
                    requestMethodAndUrl.setArgs(instanceArgs);
                }
            } catch (ClassNotFoundException | InstantiationException | IllegalAccessException |
                     NoSuchFieldException e) {
                throw new RuntimeException(e);
            }
        } else if (Objects.equals(1, requestMethodAndUrl.getBodyType())) { // 请求体类型 1.form-data
            doFormatType(requestMethodAndUrl, parameterTypes);
        }
    }

}
